home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / dns / name.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  19.2 KB  |  668 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. '''DNS Names.
  5.  
  6. @var root: The DNS root name.
  7. @type root: dns.name.Name object
  8. @var empty: The empty DNS name.
  9. @type empty: dns.name.Name object
  10. '''
  11. import cStringIO
  12. import string
  13. import struct
  14. import sys
  15. import dns.exception as dns
  16. NAMERELN_NONE = 0
  17. NAMERELN_SUPERDOMAIN = 1
  18. NAMERELN_SUBDOMAIN = 2
  19. NAMERELN_EQUAL = 3
  20. NAMERELN_COMMONANCESTOR = 4
  21.  
  22. class EmptyLabel(dns.exception.SyntaxError):
  23.     '''Raised if a label is empty.'''
  24.     pass
  25.  
  26.  
  27. class BadEscape(dns.exception.SyntaxError):
  28.     '''Raised if an escaped code in a text format name is invalid.'''
  29.     pass
  30.  
  31.  
  32. class BadPointer(dns.exception.FormError):
  33.     '''Raised if a compression pointer points forward instead of backward.'''
  34.     pass
  35.  
  36.  
  37. class BadLabelType(dns.exception.FormError):
  38.     '''Raised if the label type of a wire format name is unknown.'''
  39.     pass
  40.  
  41.  
  42. class NeedAbsoluteNameOrOrigin(dns.exception.DNSException):
  43.     '''Raised if an attempt is made to convert a non-absolute name to
  44.     wire when there is also a non-absolute (or missing) origin.'''
  45.     pass
  46.  
  47.  
  48. class NameTooLong(dns.exception.FormError):
  49.     '''Raised if a name is > 255 octets long.'''
  50.     pass
  51.  
  52.  
  53. class LabelTooLong(dns.exception.SyntaxError):
  54.     '''Raised if a label is > 63 octets long.'''
  55.     pass
  56.  
  57.  
  58. class AbsoluteConcatenation(dns.exception.DNSException):
  59.     '''Raised if an attempt is made to append anything other than the
  60.     empty name to an absolute name.'''
  61.     pass
  62.  
  63.  
  64. class NoParent(dns.exception.DNSException):
  65.     '''Raised if an attempt is made to get the parent of the root name
  66.     or the empty name.'''
  67.     pass
  68.  
  69. _escaped = {
  70.     '"': True,
  71.     '(': True,
  72.     ')': True,
  73.     '.': True,
  74.     ';': True,
  75.     '\\': True,
  76.     '@': True,
  77.     '$': True }
  78.  
  79. def _escapify(label):
  80.     '''Escape the characters in label which need it.
  81.     @returns: the escaped string
  82.     @rtype: string'''
  83.     text = ''
  84.     for c in label:
  85.         if c in _escaped:
  86.             text += '\\' + c
  87.             continue
  88.         if ord(c) > 32 and ord(c) < 127:
  89.             text += c
  90.             continue
  91.         text += '\\%03d' % ord(c)
  92.     
  93.     return text
  94.  
  95.  
  96. def _validate_labels(labels):
  97.     '''Check for empty labels in the middle of a label sequence,
  98.     labels that are too long, and for too many labels.
  99.     @raises NameTooLong: the name as a whole is too long
  100.     @raises LabelTooLong: an individual label is too long
  101.     @raises EmptyLabel: a label is empty (i.e. the root label) and appears
  102.     in a position other than the end of the label sequence'''
  103.     l = len(labels)
  104.     total = 0
  105.     i = -1
  106.     j = 0
  107.     for label in labels:
  108.         ll = len(label)
  109.         total += ll + 1
  110.         if ll > 63:
  111.             raise LabelTooLong
  112.         
  113.         if i < 0 and label == '':
  114.             i = j
  115.         
  116.         j += 1
  117.     
  118.     if total > 255:
  119.         raise NameTooLong
  120.     
  121.     if i >= 0 and i != l - 1:
  122.         raise EmptyLabel
  123.     
  124.  
  125.  
  126. class Name(object):
  127.     '''A DNS name.
  128.  
  129.     The dns.name.Name class represents a DNS name as a tuple of labels.
  130.     Instances of the class are immutable.
  131.     
  132.     @ivar labels: The tuple of labels in the name. Each label is a string of
  133.     up to 63 octets.'''
  134.     __slots__ = [
  135.         'labels']
  136.     
  137.     def __init__(self, labels):
  138.         '''Initialize a domain name from a list of labels.
  139.         @param labels: the labels
  140.         @type labels: any iterable whose values are strings
  141.         '''
  142.         super(Name, self).__setattr__('labels', tuple(labels))
  143.         _validate_labels(self.labels)
  144.  
  145.     
  146.     def __setattr__(self, name, value):
  147.         raise TypeError, "object doesn't support attribute assignment"
  148.  
  149.     
  150.     def is_absolute(self):
  151.         '''Is the most significant label of this name the root label?
  152.         @rtype: bool
  153.         '''
  154.         if len(self.labels) > 0:
  155.             pass
  156.         return self.labels[-1] == ''
  157.  
  158.     
  159.     def is_wild(self):
  160.         """Is this name wild?  (I.e. Is the least significant label '*'?)
  161.         @rtype: bool
  162.         """
  163.         if len(self.labels) > 0:
  164.             pass
  165.         return self.labels[0] == '*'
  166.  
  167.     
  168.     def __hash__(self):
  169.         '''Return a case-insensitive hash of the name.
  170.         @rtype: int
  171.         '''
  172.         h = 0x0L
  173.         for label in self.labels:
  174.             for c in label:
  175.                 h += (h << 3) + ord(c.lower())
  176.             
  177.         
  178.         return int(h % sys.maxint)
  179.  
  180.     
  181.     def fullcompare(self, other):
  182.         '''Compare two names, returning a 3-tuple (relation, order, nlabels).
  183.  
  184.         I{relation} describes the relation ship beween the names,
  185.         and is one of: dns.name.NAMERELN_NONE,
  186.         dns.name.NAMERELN_SUPERDOMAIN, dns.name.NAMERELN_SUBDOMAIN,
  187.         dns.name.NAMERELN_EQUAL, or dns.name.NAMERELN_COMMONANCESTOR
  188.  
  189.         I{order} is < 0 if self < other, > 0 if self > other, and ==
  190.         0 if self == other.  A relative name is always less than an
  191.         absolute name.  If both names have the same relativity, then
  192.         the DNSSEC order relation is used to order them.
  193.         
  194.         I{nlabels} is the number of significant labels that the two names
  195.         have in common.
  196.         '''
  197.         sabs = self.is_absolute()
  198.         oabs = other.is_absolute()
  199.         if sabs != oabs:
  200.             if sabs:
  201.                 return (NAMERELN_NONE, 1, 0)
  202.             else:
  203.                 return (NAMERELN_NONE, -1, 0)
  204.         
  205.         l1 = len(self.labels)
  206.         l2 = len(other.labels)
  207.         ldiff = l1 - l2
  208.         if ldiff < 0:
  209.             l = l1
  210.         else:
  211.             l = l2
  212.         order = 0
  213.         nlabels = 0
  214.         namereln = NAMERELN_NONE
  215.         while l > 0:
  216.             l -= 1
  217.             l1 -= 1
  218.             l2 -= 1
  219.             label1 = self.labels[l1].lower()
  220.             label2 = other.labels[l2].lower()
  221.             if label1 < label2:
  222.                 order = -1
  223.                 if nlabels > 0:
  224.                     namereln = NAMERELN_COMMONANCESTOR
  225.                 
  226.                 return (namereln, order, nlabels)
  227.             elif label1 > label2:
  228.                 order = 1
  229.                 if nlabels > 0:
  230.                     namereln = NAMERELN_COMMONANCESTOR
  231.                 
  232.                 return (namereln, order, nlabels)
  233.             
  234.             nlabels += 1
  235.         order = ldiff
  236.         if ldiff < 0:
  237.             namereln = NAMERELN_SUPERDOMAIN
  238.         elif ldiff > 0:
  239.             namereln = NAMERELN_SUBDOMAIN
  240.         else:
  241.             namereln = NAMERELN_EQUAL
  242.         return (namereln, order, nlabels)
  243.  
  244.     
  245.     def is_subdomain(self, other):
  246.         '''Is self a subdomain of other?
  247.  
  248.         The notion of subdomain includes equality.
  249.         @rtype: bool
  250.         '''
  251.         (nr, o, nl) = self.fullcompare(other)
  252.         if nr == NAMERELN_SUBDOMAIN or nr == NAMERELN_EQUAL:
  253.             return True
  254.         
  255.         return False
  256.  
  257.     
  258.     def is_superdomain(self, other):
  259.         '''Is self a superdomain of other?
  260.  
  261.         The notion of subdomain includes equality.
  262.         @rtype: bool
  263.         '''
  264.         (nr, o, nl) = self.fullcompare(other)
  265.         if nr == NAMERELN_SUPERDOMAIN or nr == NAMERELN_EQUAL:
  266.             return True
  267.         
  268.         return False
  269.  
  270.     
  271.     def canonicalize(self):
  272.         '''Return a name which is equal to the current name, but is in
  273.         DNSSEC canonical form.
  274.         @rtype: dns.name.Name object
  275.         '''
  276.         return []([ x.lower() for x in self.labels ])
  277.  
  278.     
  279.     def __eq__(self, other):
  280.         if isinstance(other, Name):
  281.             return self.fullcompare(other)[1] == 0
  282.         else:
  283.             return False
  284.  
  285.     
  286.     def __ne__(self, other):
  287.         if isinstance(other, Name):
  288.             return self.fullcompare(other)[1] != 0
  289.         else:
  290.             return True
  291.  
  292.     
  293.     def __lt__(self, other):
  294.         if isinstance(other, Name):
  295.             return self.fullcompare(other)[1] < 0
  296.         else:
  297.             return NotImplemented
  298.  
  299.     
  300.     def __le__(self, other):
  301.         if isinstance(other, Name):
  302.             return self.fullcompare(other)[1] <= 0
  303.         else:
  304.             return NotImplemented
  305.  
  306.     
  307.     def __ge__(self, other):
  308.         if isinstance(other, Name):
  309.             return self.fullcompare(other)[1] >= 0
  310.         else:
  311.             return NotImplemented
  312.  
  313.     
  314.     def __gt__(self, other):
  315.         if isinstance(other, Name):
  316.             return self.fullcompare(other)[1] > 0
  317.         else:
  318.             return NotImplemented
  319.  
  320.     
  321.     def __repr__(self):
  322.         return '<DNS name ' + self.__str__() + '>'
  323.  
  324.     
  325.     def __str__(self):
  326.         return self.to_text(False)
  327.  
  328.     
  329.     def to_text(self, omit_final_dot = False):
  330.         """Convert name to text format.
  331.         @param omit_final_dot: If True, don't emit the final dot (denoting the
  332.         root label) for absolute names.  The default is False.
  333.         @rtype: string
  334.         """
  335.         if len(self.labels) == 0:
  336.             return '@'
  337.         
  338.         if len(self.labels) == 1 and self.labels[0] == '':
  339.             return '.'
  340.         
  341.         if omit_final_dot and self.is_absolute():
  342.             l = self.labels[:-1]
  343.         else:
  344.             l = self.labels
  345.         s = string.join(map(_escapify, l), '.')
  346.         return s
  347.  
  348.     
  349.     def to_digestable(self, origin = None):
  350.         '''Convert name to a format suitable for digesting in hashes.
  351.  
  352.         The name is canonicalized and converted to uncompressed wire format.
  353.         
  354.         @param origin: If the name is relative and origin is not None, then
  355.         origin will be appended to it.
  356.         @type origin: dns.name.Name object
  357.         @raises NeedAbsoluteNameOrOrigin: All names in wire format are
  358.         absolute.  If self is a relative name, then an origin must be supplied;
  359.         if it is missing, then this exception is raised
  360.         @rtype: string
  361.         '''
  362.         if not self.is_absolute():
  363.             if origin is None or not origin.is_absolute():
  364.                 raise NeedAbsoluteNameOrOrigin
  365.             
  366.             labels = list(self.labels)
  367.             labels.extend(list(origin.labels))
  368.         else:
  369.             labels = self.labels
  370.         dlabels = [ '%s%s' % (chr(len(x)), x.lower()) for x in labels ]
  371.         return ''.join(dlabels)
  372.  
  373.     
  374.     def to_wire(self, file = None, compress = None, origin = None):
  375.         '''Convert name to wire format, possibly compressing it.
  376.  
  377.         @param file: the file where the name is emitted (typically
  378.         a cStringIO file).  If None, a string containing the wire name
  379.         will be returned.
  380.         @type file: file or None
  381.         @param compress: The compression table.  If None (the default) names
  382.         will not be compressed.
  383.         @type compress: dict
  384.         @param origin: If the name is relative and origin is not None, then
  385.         origin will be appended to it.
  386.         @type origin: dns.name.Name object
  387.         @raises NeedAbsoluteNameOrOrigin: All names in wire format are
  388.         absolute.  If self is a relative name, then an origin must be supplied;
  389.         if it is missing, then this exception is raised
  390.         '''
  391.         if file is None:
  392.             file = cStringIO.StringIO()
  393.             want_return = True
  394.         else:
  395.             want_return = False
  396.         if not self.is_absolute():
  397.             if origin is None or not origin.is_absolute():
  398.                 raise NeedAbsoluteNameOrOrigin
  399.             
  400.             labels = list(self.labels)
  401.             labels.extend(list(origin.labels))
  402.         else:
  403.             labels = self.labels
  404.         i = 0
  405.         for label in labels:
  406.             n = Name(labels[i:])
  407.             i += 1
  408.             if compress is not None:
  409.                 pos = compress.get(n)
  410.             else:
  411.                 pos = None
  412.             if pos is not None:
  413.                 value = 49152 + pos
  414.                 s = struct.pack('!H', value)
  415.                 file.write(s)
  416.                 break
  417.                 continue
  418.             if compress is not None and len(n) > 1:
  419.                 pos = file.tell()
  420.                 if pos < 49152:
  421.                     compress[n] = pos
  422.                 
  423.             
  424.             l = len(label)
  425.             file.write(chr(l))
  426.             if l > 0:
  427.                 file.write(label)
  428.                 continue
  429.         
  430.         if want_return:
  431.             return file.getvalue()
  432.         
  433.  
  434.     
  435.     def __len__(self):
  436.         '''The length of the name (in labels).
  437.         @rtype: int
  438.         '''
  439.         return len(self.labels)
  440.  
  441.     
  442.     def __getitem__(self, index):
  443.         return self.labels[index]
  444.  
  445.     
  446.     def __getslice__(self, start, stop):
  447.         return self.labels[start:stop]
  448.  
  449.     
  450.     def __add__(self, other):
  451.         return self.concatenate(other)
  452.  
  453.     
  454.     def __sub__(self, other):
  455.         return self.relativize(other)
  456.  
  457.     
  458.     def split(self, depth):
  459.         '''Split a name into a prefix and suffix at depth.
  460.  
  461.         @param depth: the number of labels in the suffix
  462.         @type depth: int
  463.         @raises ValueError: the depth was not >= 0 and <= the length of the
  464.         name.
  465.         @returns: the tuple (prefix, suffix)
  466.         @rtype: tuple
  467.         '''
  468.         l = len(self.labels)
  469.         if depth == 0:
  470.             return (self, dns.name.empty)
  471.         elif depth == l:
  472.             return (dns.name.empty, self)
  473.         elif depth < 0 or depth > l:
  474.             raise ValueError, 'depth must be >= 0 and <= the length of the name'
  475.         
  476.         return (Name(self[:-depth]), Name(self[-depth:]))
  477.  
  478.     
  479.     def concatenate(self, other):
  480.         '''Return a new name which is the concatenation of self and other.
  481.         @rtype: dns.name.Name object
  482.         @raises AbsoluteConcatenation: self is absolute and other is
  483.         not the empty name
  484.         '''
  485.         if self.is_absolute() and len(other) > 0:
  486.             raise AbsoluteConcatenation
  487.         
  488.         labels = list(self.labels)
  489.         labels.extend(list(other.labels))
  490.         return Name(labels)
  491.  
  492.     
  493.     def relativize(self, origin):
  494.         '''If self is a subdomain of origin, return a new name which is self
  495.         relative to origin.  Otherwise return self.
  496.         @rtype: dns.name.Name object
  497.         '''
  498.         if origin is not None and self.is_subdomain(origin):
  499.             return Name(self[:-len(origin)])
  500.         else:
  501.             return self
  502.  
  503.     
  504.     def derelativize(self, origin):
  505.         '''If self is a relative name, return a new name which is the
  506.         concatenation of self and origin.  Otherwise return self.
  507.         @rtype: dns.name.Name object
  508.         '''
  509.         if not self.is_absolute():
  510.             return self.concatenate(origin)
  511.         else:
  512.             return self
  513.  
  514.     
  515.     def choose_relativity(self, origin = None, relativize = True):
  516.         '''Return a name with the relativity desired by the caller.  If
  517.         origin is None, then self is returned.  Otherwise, if
  518.         relativize is true the name is relativized, and if relativize is
  519.         false the name is derelativized.
  520.         @rtype: dns.name.Name object
  521.         '''
  522.         if origin:
  523.             if relativize:
  524.                 return self.relativize(origin)
  525.             else:
  526.                 return self.derelativize(origin)
  527.         else:
  528.             return self
  529.  
  530.     
  531.     def parent(self):
  532.         '''Return the parent of the name.
  533.         @rtype: dns.name.Name object
  534.         @raises NoParent: the name is either the root name or the empty name,
  535.         and thus has no parent.
  536.         '''
  537.         if self == root or self == empty:
  538.             raise NoParent
  539.         
  540.         return Name(self.labels[1:])
  541.  
  542.  
  543. root = Name([
  544.     ''])
  545. empty = Name([])
  546.  
  547. def from_text(text, origin = root):
  548.     '''Convert text into a Name object.
  549.     @rtype: dns.name.Name object
  550.     '''
  551.     if not isinstance(text, str):
  552.         raise ValueError, 'input to from_text() must be a byte string'
  553.     
  554.     if not origin is None or isinstance(origin, Name):
  555.         raise ValueError, 'origin must be a Name or None'
  556.     
  557.     labels = []
  558.     label = ''
  559.     escaping = False
  560.     edigits = 0
  561.     total = 0
  562.     if text == '@':
  563.         text = ''
  564.     
  565.     if text:
  566.         if text == '.':
  567.             return Name([
  568.                 ''])
  569.         
  570.         for c in text:
  571.             if escaping:
  572.                 if edigits == 0:
  573.                     if c.isdigit():
  574.                         total = int(c)
  575.                         edigits += 1
  576.                     else:
  577.                         label += c
  578.                         escaping = False
  579.                 elif not c.isdigit():
  580.                     raise BadEscape
  581.                 
  582.                 total *= 10
  583.                 total += int(c)
  584.                 edigits += 1
  585.                 if edigits == 3:
  586.                     escaping = False
  587.                     label += chr(total)
  588.                 
  589.             edigits == 3
  590.             if c == '.':
  591.                 if len(label) == 0:
  592.                     raise EmptyLabel
  593.                 
  594.                 labels.append(label)
  595.                 label = ''
  596.                 continue
  597.             if c == '\\':
  598.                 escaping = True
  599.                 edigits = 0
  600.                 total = 0
  601.                 continue
  602.             label += c
  603.         
  604.         if escaping:
  605.             raise BadEscape
  606.         
  607.         if len(label) > 0:
  608.             labels.append(label)
  609.         else:
  610.             labels.append('')
  611.     
  612.     if (len(labels) == 0 or labels[-1] != '') and origin is not None:
  613.         labels.extend(list(origin.labels))
  614.     
  615.     return Name(labels)
  616.  
  617.  
  618. def from_wire(message, current):
  619.     '''Convert possibly compressed wire format into a Name.
  620.     @param message: the entire DNS message
  621.     @type message: string
  622.     @param current: the offset of the beginning of the name from the start
  623.     of the message
  624.     @type current: int
  625.     @raises dns.name.BadPointer: a compression pointer did not point backwards
  626.     in the message
  627.     @raises dns.name.BadLabelType: an invalid label type was encountered.
  628.     @returns: a tuple consisting of the name that was read and the number
  629.     of bytes of the wire format message which were consumed reading it
  630.     @rtype: (dns.name.Name object, int) tuple
  631.     '''
  632.     if not isinstance(message, str):
  633.         raise ValueError, 'input to from_wire() must be a byte string'
  634.     
  635.     labels = []
  636.     biggest_pointer = current
  637.     hops = 0
  638.     count = ord(message[current])
  639.     current += 1
  640.     cused = 1
  641.     while count != 0:
  642.         if count < 64:
  643.             labels.append(message[current:current + count])
  644.             current += count
  645.             if hops == 0:
  646.                 cused += count
  647.             
  648.         elif count >= 192:
  649.             current = (count & 63) * 256 + ord(message[current])
  650.             if hops == 0:
  651.                 cused += 1
  652.             
  653.             if current >= biggest_pointer:
  654.                 raise BadPointer
  655.             
  656.             biggest_pointer = current
  657.             hops += 1
  658.         else:
  659.             raise BadLabelType
  660.         count = ord(message[current])
  661.         current += 1
  662.         if hops == 0:
  663.             cused += 1
  664.             continue
  665.     labels.append('')
  666.     return (Name(labels), cused)
  667.  
  668.